package com.sitech.ibnms.c3p.command.script;

import com.sitech.ibnms.c3p.command.CommandParamsService;
import com.sitech.ibnms.c3p.command.CommandType;
import com.sitech.ibnms.c3p.domain.C3pTaskResult;
import com.sitech.ibnms.c3p.domain.C3pTaskResultBaseLine;
import com.sitech.ibnms.c3p.domain.ResourceAccount;
import com.sitech.ibnms.c3p.form.TextDiff;
import com.sitech.ibnms.c3p.mappers.C3pTaskResultBaseLineMapper;
import com.sitech.ibnms.c3p.task.C3pServerLogTaskService;
import com.sitech.ibnms.c3p.task.C3pTaskFlowServiceExtends;
import com.sitech.ibnms.c3p.task.aspect.SQLResultToMailProcesser;
import com.sitech.ibnms.c3p.task.notice.NoticeHandleService;
import com.sitech.ibnms.c3p.util.C3pConstants;
import com.sitech.ibnms.c3p.util.JSONUtil;
import com.sitech.ibnms.c3p.util.TextDiffUtil;
import com.sitech.ismp.messageObject.cc.CccpConstants;
import com.sitech.ismp.messageObject.cc.CccpResults;
import com.sitech.ismp.workflow.domain.Task;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.*;

import static org.apache.commons.lang3.time.DateFormatUtils.format;

/**
 * 接收执行器执行结果
 * Created by liujhc on 7/22.
 */
@Component
public class ScriptResultMessageListener {
    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private CommandParamsService paramsService;
    @Autowired
    private C3pTaskFlowServiceExtends taskFlowServiceExtends;
    @Autowired
    private C3pServerLogTaskService logTaskService;
    @Autowired
    private SQLResultToMailProcesser mailProcesser;
    @Autowired
    private NoticeHandleService noticeHandleService;
    @Autowired
    private C3pTaskResultBaseLineMapper c3pTaskResultBaseLineMapper;
    public void processResult(CccpResults results,ResourceAccount resourceAccount, C3pTaskResult taskResult,String taskType) {
        // 将所有执行结果属性加入到输出变量中入库
        HashMap<String, String> params = results.getParams();
        String resourceId = params.get(C3pConstants.PARAM_EXECUTE_RESOURCE_ID);
        logger.info("CccpResults-{}: {}", resourceId, JSONUtil.toJSON(results));

        Map<String, String> resultMap = results.getVariables();
        Map<String, String> outVars = new HashMap<String, String>();
        if (resultMap != null) {
            outVars.putAll(resultMap);
        }
        addColValForAimTask(results,resourceAccount,taskResult,taskType);
        outVars.put(C3pConstants.CMD_OUT_RESULT_TYPE + "(" + resourceId + ")", results.getResultType());
        outVars.put(C3pConstants.CMD_OUT_BEGIN_TIME + "(" + resourceId + ")", results.getBeginTime());
        outVars.put(C3pConstants.CMD_OUT_END_TIME + "(" + resourceId + ")", results.getEndTime());
        outVars.put(C3pConstants.CMD_OUT_EXEC_FLAG + "(" + resourceId + ")", String.valueOf(results.getExecFlag()));
        outVars.put(C3pConstants.CMD_OUT_EXEC_RESULT + "(" + resourceId + ")", results.getExecResult());
        outVars.put(C3pConstants.CMD_OUT_FAILED_REASON + "(" + resourceId + ")", results.getFailedReason());
        outVars.put(C3pConstants.CMD_OUT_KPIS + "(" + resourceId + ")", JSONUtil.toJSON(results.getKpis()));

        Assert.notNull(params, "Unable to continue processing, because the property 'params' of CccpResults is null !");

        String c3pTaskId = params.get(C3pConstants.PARAM_C3P_TASK_ID);
        Task task = JSONUtil.fromJSON(params.get(C3pConstants.PARAM_WORKFLOW_TASK), Task.class);

        if (task != null) {
            logger.info("Task is not null, save outVars and submitNode, Task({}@{}) is created {} seconds ago and The This script is executed begin {} to {}.",
                    task.getTaskId(),
                    task.getSerialNo(),
                    (System.currentTimeMillis() - task.getDateCreated().getTime()) / 1000,
                    results.getBeginTime(), results.getEndTime());
            try {
                if (!StringUtils.isBlank(outVars.get(C3pConstants.OUTVAR_SQL_RESULT_CONTENT))) {
                    mailProcesser.process(task, params, outVars,resourceAccount);
                }
                if (!StringUtils.isBlank(outVars.get(C3pConstants.OUTVAR_SHELL_RESULT_CONTENT))) {
                    mailProcesser.saveIspShellResult(task, params, outVars,resourceAccount);
                }
                paramsService.saveParamsOut(c3pTaskId, task, outVars.entrySet());
                if (results.getExecFlag() == -1) {
                    String failedReason = String.format("Script execution failed: %s", results.getFailedReason());
                    logger.info(failedReason);
                    logTaskService.writeTaskNodeLog(task, "环节【${nodeName}】脚本执行失败(" + resourceId + ")：,失败原因" +
                            results.getFailedReason());
                } else {
                    logger.info("Script execution success: {}", results.getExecResult());
                    logTaskService.writeTaskNodeLog(task, "环节【${nodeName}】脚本执行成功(" + resourceId + ")，执行结果：\n" + results.getExecResult());
                }

                // 更新环节执行完成的脚本数量，并返回更新后的值
                //这个方法是事务的，会保持一致性
                int completeResourceCount = paramsService.updateCompleteResourceCount(task);
                // IO表中已经不存在本次任务的数据
                if (completeResourceCount < 0) {
                    logger.error("收到指令执行结果，但任务已结束[serialNo={}, taskId={}]", task.getSerialNo(), task.getTaskId());
                    return;
                }

                /**所有环节执行完毕后 判断该环节中是否有执行失败的主机  如果有 则进行失败策略  如果没有  则提交该环节*/
                // 获取环节应该完成的数量
                int paramResourceCount = paramsService.getParamResourceCount(task);
                logger.info("Task[serialNo={},taskId={}] executed {} of all {} scripts", task.getSerialNo(), task.getTaskId(), completeResourceCount, paramResourceCount);
                // 所有主机都执行完毕
                //这样做的好处是没有线程等待 ，是数据 推动
                if (paramResourceCount == completeResourceCount) {
                    int failResourceCount = paramsService.findNodeExecuteFailCount(task);
                    if (failResourceCount == 0) { // 无失败记录，提交环节
                        taskFlowServiceExtends.submitNode(task.getTaskId(), task.getCurrDealer(), task.getTitle());
                    } else if (paramResourceCount == 1) { // 该环节只有一个执行主机，失败直接走失败策略，提供脚本执行失败原因
                        taskFlowServiceExtends.failPolicy(task, new IllegalStateException(results.getFailedReason()));
                    } else { // 环节有多个执行主机，有失败走失败策略，并说明失败情况
                        String resourceTypeName = CommandType.SHELL.name().equals(params.get(C3pConstants.PARAM_COMMAND_TYPE)) ? "主机" : "数据库";
                        taskFlowServiceExtends.failPolicy(task, new IllegalStateException("配置的"
                                + paramResourceCount + "个执行" + resourceTypeName + "中，有" + failResourceCount + "个执行失败!"));
                    }
                }
            } catch (Exception e) {
                taskFlowServiceExtends.failPolicy(task, e);
            }
        } else {
            logger.error("The Task object in Message is null, requestId = " + params.get(CccpConstants.PARAM_REQUEST_ID));
        }
    }
    private void addColValForAimTask(CccpResults results,ResourceAccount resourceAccount, C3pTaskResult taskResult,String taskType){
        String dateTime=format(new Date(), "yyyy-MM-dd HH:mm:ss");
        if("allResult".equalsIgnoreCase(taskType)||"logInspection".equalsIgnoreCase(taskType)||"script".equalsIgnoreCase(taskType)){
            results.getKpis().add(autoAddMap(resourceAccount.getUnitId(),C3pConstants.KPI_SCRIPT_RUN_STATUS,results.getExecFlag()!=-1?"Success":"Failed",dateTime,resourceAccount.getUnitId(),"","0","0"));
        }
        if("allResult".equalsIgnoreCase(taskType)){
            String message=dealTaskResultNotice(results,taskResult);
            results.getKpis().add(autoAddMap(resourceAccount.getUnitId(),C3pConstants.KPI_RESULT_DIFF,message.isEmpty()?"NoChange":"Changed",dateTime,resourceAccount.getUnitId(),"","0","0"));
        }
    }
    private String dealTaskResultNotice(CccpResults result,C3pTaskResult taskResult){
        String resultMessage="";
        try{
            String resultStr=result.getExecFlag()==-1?result.getFailedReason():result.getExecResult();
            if(resultStr==null){
                resultStr="";
            }
            //根据taskid获取基线配置数据
            List<C3pTaskResultBaseLine> c3pTaskResultBaseLines=c3pTaskResultBaseLineMapper.queryTaskResultBaseLine(taskResult.getTaskId());
            if(c3pTaskResultBaseLines==null||c3pTaskResultBaseLines.isEmpty()){
                return "".equals(resultStr)?"":"Changed";
            }
            List<String> configLines=new ArrayList<String>();
            StringBuffer newText = new StringBuffer();
            for (C3pTaskResultBaseLine base : c3pTaskResultBaseLines) {
                configLines.add(base.getExcuteResult()==null?"\n":base.getExcuteResult()+"\n");
            }
            String [] resultStrs=resultStr.split("\n");
            List<String> newLines=new ArrayList<String>();
            for(String str:resultStrs){
                newLines.add(str==null?"\n":str+"\n");
            }
            List<List<TextDiff>> diffResults = TextDiffUtil.getDiff(configLines,newLines);
            for(C3pTaskResultBaseLine domain : c3pTaskResultBaseLines){
                String domainResultRule=domain.getResultRule();
                if (domainResultRule!=null&&!"[]".equals(domainResultRule)) {
                    List<Map<String, String>> obj = (List<Map<String, String>>) (JSONUtil.fromJSON(domainResultRule));
                    for (Map<String, String> map : obj) {
                        String key = map.get("KEY");
                        List<TextDiff> textDiffs = diffResults.get(domain.getRowNo() - 1);
                        TextDiffUtil.mergeFind(textDiffs, key, domain.getExcuteResult() + "\n");
                    }
                }
            }
            newText.setLength(0);
            String key="",content="",key_now;
            boolean waitAppend=false;
            for (List<TextDiff> row : diffResults) {
                for (TextDiff texts : row) {
                    key_now=texts.getKey();
                    if(texts.isUsed()&&key_now!=null&&!key_now.isEmpty()){//处理 被用到的，key不为空的，即有关键字对应的
                        if(key.equals(key_now)){//如果key相同，则合并内容，即可能，是关键字key，对应的删除，新增等动作对应的字符串合并
                            content+=texts.getNewText();
                            waitAppend=true;
                        }else {//处理 key，都不为空，且不同的情况，即相邻的key，
                            newText.append(TextDiffUtil.returnMessage(key,content));
                            key=key_now;
                            content=texts.getNewText();
                            waitAppend=true;
                        }
                    }else{
                        if(waitAppend){
                            newText.append(TextDiffUtil.returnMessage(key,content));
                            key="";
                            content="";
                        }
                    }
                }
            }
            resultMessage = newText.length() > 1?newText.substring(0, newText.length() - 1):"";
            noticeHandleService.sendTaskResultNotice(taskResult,"SMS",resultMessage);
        }catch (Exception e){
            logger.error("sendTaskResultNotice error",e);
        }finally {
            return resultMessage;
        }
    }
    private HashMap<String, String> autoAddMap(String UNIT_ID,String KPI_ID,String KPI_VALUE,String CLL_TIME,String EXT_UNIT_ID,String POSITION,String COL_NAME,String DISPLAY_MODE){
        final HashMap<String, String> kpi = new HashMap<String, String>();
        kpi.put("UNIT_ID",UNIT_ID);
        kpi.put("KPI_ID", KPI_ID);
        kpi.put("KPI_VALUE",KPI_VALUE);
        kpi.put("CLL_TIME",CLL_TIME);
        kpi.put("EXT_UNIT_ID",EXT_UNIT_ID);
        kpi.put("POSITION",POSITION);
        kpi.put("COL_NAME",COL_NAME);
        kpi.put("DISPLAY_MODE",DISPLAY_MODE);
        return kpi;
    }
}